package com.example.emailWeb.ntru.demo;

import com.example.emailWeb.ntru.encrypt.NtruProxyParameters;
import com.example.emailWeb.ntru.encrypt.NtruProxyServer;
import com.example.emailWeb.ntru.encrypt.NtruProxyUserA;
import com.example.emailWeb.ntru.encrypt.NtruProxyUserB;
import com.example.emailWeb.ntru.polynomial.IntegerPolynomial;

public class ProxySimple {
    //    NtruProxyParameters APR2011_439
    public static IntegerPolynomial verifyRKab(IntegerPolynomial fA, IntegerPolynomial invertfB, IntegerPolynomial e, int p, int q) {
        IntegerPolynomial res1, res2;
        IntegerPolynomial tmpfA = fA;
        IntegerPolynomial E = e.clone();
        E.mult(p);//p*e
        res2 = E.mult(invertfB);//p*e*invertfB
        res1 = tmpfA.mult(invertfB);
        res1.add(res2, q);
        return res1;
    }

    public static IntegerPolynomial verifyCB(IntegerPolynomial RKab, int p, int q, IntegerPolynomial gA, IntegerPolynomial invertfA, IntegerPolynomial R2, byte[] M) {
        int N = 439, maxM1 = 126;
        IntegerPolynomial mTrin = IntegerPolynomial.fromBinary3Sves(M, N, maxM1 > 0);   // don't use the constant coeff if maxM1 is set; see below
        IntegerPolynomial res1, res2;
        res2 = gA.clone();
        res2 = res2.mult(invertfA);//gA * invertfA
        res2 = res2.mult(R2);// gA * invertfA * R2
        res2.mult(p); // p * gA * invertfA * R2
//        mTrin.modPositive(3);
        res2.add(mTrin); // p * gA * invertfA * R2 + m
        res1 = RKab.clone();
        return res1.mult(res2, q); // RKab * (p * gA * invertfA * R2 + m)(mod q)
    }

    public static IntegerPolynomial verifyCbSKbModQ(int p, int q, IntegerPolynomial gA, IntegerPolynomial r2, IntegerPolynomial e1, IntegerPolynomial invertfA, IntegerPolynomial fA, byte[] m) {
        int N = 439, maxM1 = 126;
        IntegerPolynomial mTrin = IntegerPolynomial.fromBinary3Sves(m, N, maxM1 > 0);   // don't use the constant coeff if maxM1 is set; see below
        System.out.println("mTrin");
        System.out.println(mTrin);
        IntegerPolynomial res1 = gA.clone();
        res1.mult(3);//p*gA
        res1 = res1.mult(r2);//p*gA*r2

        IntegerPolynomial res2 = e1.clone();//e1
        res2.mult(3); // p*e
        System.out.println("res2" + res2);
        res2.mult(3); //e1*p*p
        System.out.println("res2" + res2);
        res2 = res2.mult(gA); //e1*p*p*gA
        System.out.println("res2" + res2);
        res2 = res2.mult(invertfA); //
        System.out.println("res2" + res2);
        res2 = res2.mult(r2);
        System.out.println("res2" + res2);

        IntegerPolynomial res3 = mTrin.clone();
        res3 = res3.mult(fA);
//        res3.center0();

        IntegerPolynomial res4 = e1.clone();
        res4.mult(3);
        res4 = res4.mult(mTrin);

        IntegerPolynomial res = res1.clone();
        res.add(res2);
        res.add(res3);
        res.add(res4);
//        res.center0(q);
        res.mod3();
        return res;

    }

    public static IntegerPolynomial SimplefaM(IntegerPolynomial fA, byte[] m, int N, int p, int q, int maxM1) {

        IntegerPolynomial mTrin = IntegerPolynomial.fromBinary3Sves(m, N, maxM1 > 0);   // don't use the constant coeff if maxM1 is set; see below
        System.out.println("SImplefaM mTrin:");
        System.out.println(mTrin);
//        mTrin.modPositive(3);//为什么就加这一句，就稳定了
        IntegerPolynomial fAClone = fA.clone();
//        fAClone.modPositive(3);
        System.out.println("fA: " + fAClone);

        IntegerPolynomial res = fAClone.mult(mTrin, q);
        System.out.println("fA*m(mod q): ");
        System.out.println(res);
        res.center0(q);//很重要
        System.out.println("center0: ");
        System.out.println(res);
        res.mod3();
        return res;
    }

    public static IntegerPolynomial pgr(int p, int q, IntegerPolynomial gA, IntegerPolynomial r2, IntegerPolynomial e1, IntegerPolynomial invertfA, IntegerPolynomial fA, byte[] m) {
        IntegerPolynomial res1 = gA.clone();
        System.out.println("gA: " + gA);
        System.out.println("r2: " + r2);
        res1.mult(3);//p*gA
        res1 = res1.mult(r2);//p*gA*r2
        IntegerPolynomial pgrClone = res1.clone();
        System.out.println("res1 start: " + res1);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        System.out.println("res1: " + res1);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        res1.add(res1, q);
        System.out.println("res1: " + res1);
//        res1.center0(q);
        res1.mod3();
        return res1;
    }

    public static void main(String[] args) {

        NtruProxyUserA UserA = new NtruProxyUserA(NtruProxyParameters.APR2011_439);
        NtruProxyUserB UserB = new NtruProxyUserB(NtruProxyParameters.APR2011_439);
        NtruProxyServer Server = new NtruProxyServer(NtruProxyParameters.APR2011_439);
        NtruProxyParameters param = NtruProxyParameters.APR2011_439;
        int N = param.N, p = param.p, q = param.q, maxM1 = 126;
        System.out.println();
        System.out.println("************密钥协商************");
        Server.receiveR(UserA.r1);
        System.out.println("代理服务器收到A发来的r: " + UserA.r1);
        UserB.receiveA(UserA.a);
        System.out.println("用户B收到用户A发的a: " + UserA.a);
        Server.receiveB(UserB.b);
        System.out.println("代理服务器收到用户B发的b: " + UserB.b);
        System.out.println("代理服务器计算RKab：" + Server.Rkab);

//        IntegerPolynomial RKab2 = verifyRKab(UserA.fA, UserB.invertfB, UserA.e, p, q);
//        System.out.println("RKab2:");
//        System.out.println(RKab2);

//        //加密
        System.out.println();
        System.out.println("************NTRU代理重加密过程************");
//        String msg = "Hello UserB，123456。";
        String msg = "Hello************NTRU代理重加密过程************end.";
        System.out.println("用户A打算发给用户B的明文：" + msg);
//        IntegerPolynomial CA = UserA.generateCA(msg.getBytes());
        IntegerPolynomial CA = UserA.generateCA(msg);
        System.out.println("用户A生成CA发给代理服务器: " + CA);
        IntegerPolynomial CB = Server.CA2CB(CA);
        System.out.println("服务器通过CA生成CB，CB发给用户B：" + CB);

        String FaM = UserB.decrypt(CB);
        System.out.println("用户B通过CB恢复出，A发的消息: " + FaM);


//        IntegerPolynomial res = UserB.fB.mult(UserB.invertfB, q);
//        System.out.println("res: "+res);

//        System.out.println("User.gA: " + UserA.gA);
//        System.out.println("User.r2 " + UserA.r2);
//        System.out.println("User.e " + UserA.e);
//        System.out.println("User.invertfA "+UserA.invertfA);
//        System.out.println("User.fA "+UserA.fA);
//        System.out.println("User.fB: "+UserB.fB);

//        IntegerPolynomial FM = verifyCbSKbModQ(p, q, UserA.gA, UserA.r2, UserA.e, UserA.invertfA, UserA.fA, msg.getBytes());
//        System.out.println("Verify verifyCbSKbModQ:");
//        System.out.println(FM);

//        System.out.println("fA * invertfA(mod q): ");
//        System.out.println(UserA.fA.mult(UserA.invertfA,q));
//        System.out.println("fA mod p");
//        IntegerPolynomial fAClone = UserA.fA.clone();
//        fAClone.mod3();
//        System.out.println(fAClone);
//
//        System.out.println("fB * invertfB(mod q): ");
//        System.out.println(UserB.fB.mult(UserB.invertfB,q));

//        System.out.println("pgr:");
//        IntegerPolynomial pgrRes = pgr(p, q, UserA.gA, UserA.r2, UserA.e, UserA.invertfA, UserA.fA, msg.getBytes());
//        System.out.println(pgrRes);
//        System.out.println("Simple fA * m: ");
//        IntegerPolynomial SimpleM = SimplefaM(UserA.fA, msg.getBytes(), N, p,q, maxM1);
//        System.out.println("m of sinple: ");
//        System.out.println(SimpleM);
    }
}
